#include "collisionshapetools.h"
#include "../editors/widgets/editcollisionshapesview.h"
#include "../physics/collisionshapesdocument.h"
#include "../physics/collisionshapes.h"
#include "../physics/shapemanipulators.h"
#include <QPainter>
#include <QMouseEvent>
#include <math.h>

QMap<QString, QPointer<CollisionShapeTool> >CollisionShapeTool::m_tools;

CollisionShapeTool::CollisionShapeTool(QWidget *view) :
    QObject(view)
   , m_bInProgress(false)
{
    QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
    Q_ASSERT(csd);
    connect(this, SIGNAL(newObjectCreationStarted()),
            csd, SLOT(deselectAllShapes()));
}

CollisionShapeTool::~CollisionShapeTool()
{

}

QPointer<EditCollisionShapesView> CollisionShapeTool::parentView() const
{
    EditCollisionShapesView* pParentView = qobject_cast<EditCollisionShapesView*>(parent());
    Q_ASSERT(pParentView);
    return pParentView;
}

bool CollisionShapeTool::isInProgress() const
{
    return m_bInProgress;
}

void CollisionShapeTool::setInProgress(bool b)
{
    m_bInProgress = b;
}

void CollisionShapeTool::transformToEditingViewCoordinates(QPointF &point)
{
    Q_ASSERT(parentView());
    point.setX(point.x() - parentView()->viewOrigin().x());
    point.setY(point.y() - parentView()->viewOrigin().y());
    point /= parentView()->zoomFactor();
}


void CollisionShapeTool::mousePress(QMouseEvent *pEvent)
{

}

void CollisionShapeTool::mouseMove(QMouseEvent *pEvent)
{

}

void CollisionShapeTool::mouseRelease(QMouseEvent *pEvent)
{

}

void CollisionShapeTool::draw(QPainter &painter) const
{

}

// Tools initialization
bool CollisionShapeTool::initTools(QWidget *view)
{
    m_tools["SelectAndManipulate"] = new SelectAndManipulateCST(view);
    m_tools["Select"] = new SelectCST(view);
    m_tools["CreateCircle"] = new CreateCircleCST(view);
    m_tools["CreateRectangle"] = new CreateRectangleCST(view);
    m_tools["CreatePoly"] = new CreatePolyCST(view);

    return true;
}

QPointer<CollisionShapeTool> CollisionShapeTool::toolByName(const QString &name)
{
    QPointer<CollisionShapeTool> result;

    if(m_tools.find(name) == m_tools.end())
    {
        Q_ASSERT(false);
    }
    else
    {
        result = m_tools[name];
    }

    return result;
}

// Select and manipulate

SelectAndManipulateCST::SelectAndManipulateCST(QWidget *view)
    : CollisionShapeTool(view)
{
    m_manipulatorEngine.reset(new ShapeManipulatorEngine);

    QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
    connect(csd, SIGNAL(editingObjectChanged()),
            this, SLOT(exitManipulationMode()));
}

void SelectAndManipulateCST::enter()
{
    QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
    QList<QPointer<CollisionShape> > selection = csd->selectedShapes();

    if(selection.size() == 1)
    {
        m_manipulatorEngine->setShape(selection.first());
        parentView()->update();
    }
}

void SelectAndManipulateCST::exit()
{
    m_manipulatorEngine->setShape(0);
}

void SelectAndManipulateCST::mousePress(QMouseEvent *pEvent)
{
//    if(pEvent->buttons() & Qt::LeftButton)
//    {
//        setInProgress(true);
//        m_origin = pEvent->posF();
//        transformToEditingViewCoordinates(m_origin);
//        m_selectionRect = QRectF(m_origin, QSize(1, 1));
//        parentView()->update();
//    }

    QPointF transformedPoint = pEvent->posF();
    transformToEditingViewCoordinates(transformedPoint);

    QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
    QPointer<CollisionShape> shapeUnderMouse = csd->shapeUnderMouse(transformedPoint);

    if(m_manipulatorEngine->manipulationMode())
    {
        if(!m_manipulatorEngine->mousePress(transformedPoint))
        {
            QPointer<CollisionShape> selectedShape = csd->selectedShapes().first();

            if(selectedShape != shapeUnderMouse)
            {
                m_manipulatorEngine->setShape(0);
            }
        }

        parentView()->update();
    }

    if(!m_manipulatorEngine->manipulationMode())
    {
        csd->deselectAllShapes();

        if(shapeUnderMouse)
        {
            csd->selectShape(shapeUnderMouse);
            m_manipulatorEngine->setShape(shapeUnderMouse);
        }
    }
}

void SelectAndManipulateCST::mouseMove(QMouseEvent *pEvent)
{
    QPointF transformedPoint = pEvent->posF();
    transformToEditingViewCoordinates(transformedPoint);

    if(m_manipulatorEngine->manipulationMode())
    {
        m_manipulatorEngine->mouseMove(transformedPoint);
        parentView()->update();
    }
}

void SelectAndManipulateCST::mouseRelease(QMouseEvent *pEvent)
{
    QPointF transformedPoint = pEvent->posF();
    transformToEditingViewCoordinates(transformedPoint);

    if(m_manipulatorEngine->manipulationMode())
    {
        m_manipulatorEngine->mouseRelease(transformedPoint);
        parentView()->update();
    }
}

void SelectAndManipulateCST::enterManipulationMode()
{
    QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
    QList<QPointer<CollisionShape> > selectedShapes = csd->selectedShapes();

    Q_ASSERT(selectedShapes.size() == 1);
    Q_ASSERT(m_manipulatorEngine);
    m_manipulatorEngine->setShape(selectedShapes.first());
}

void SelectAndManipulateCST::exitManipulationMode()
{
    m_manipulatorEngine->setShape(0);
}

void SelectAndManipulateCST::draw(QPainter &painter) const
{
    CollisionShapeTool::draw(painter);

    if(m_manipulatorEngine->manipulationMode())
    {
        m_manipulatorEngine->drawManipulator(painter);
        parentView()->update();
    }
}

// Create circle

CreateCircleCST::CreateCircleCST(QWidget *view)
    : CollisionShapeTool(view)
    , m_fRadius(0.0f)
{

}

void CreateCircleCST::enter()
{
    Q_ASSERT(parentView());
    parentView()->setCursor(Qt::CrossCursor);
}

void CreateCircleCST::exit()
{
    parentView()->setCursor(Qt::ArrowCursor);
}

void CreateCircleCST::mousePress(QMouseEvent *pEvent)
{
    if(pEvent->buttons() & Qt::LeftButton)
    {
        setInProgress(true);
        m_center = pEvent->posF();

        transformToEditingViewCoordinates(m_center);

        emit newObjectCreationStarted();

        m_fRadius = 0.0f;
        parentView()->update();
    }
}

void CreateCircleCST::mouseMove(QMouseEvent *pEvent)
{
    if((pEvent->buttons() & Qt::LeftButton) && isInProgress())
    {
        QPointF mousePos = pEvent->posF();
        transformToEditingViewCoordinates(mousePos);

        QPointF v = mousePos - m_center;
        m_fRadius = sqrt(v.x()*v.x() + v.y()*v.y());
        parentView()->update();
    }
}

void CreateCircleCST::mouseRelease(QMouseEvent *pEvent)
{
    if(isInProgress())
    {
        if(m_fRadius > 1.0f)
        {
            // create circle shape
            QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
            Q_ASSERT(csd);

            QPointer<CollisionShape> cs = csd->createCircleShape(m_center, m_fRadius);
        }
        else
        {
            emit actionCanceled();
        }

        setInProgress(false);
        parentView()->update();
    }
}

void CreateCircleCST::draw(QPainter &painter) const
{
    CollisionShapeTool::draw(painter);

    if(isInProgress())
    {
        QPen pen(Qt::green, 1, Qt::DotLine);
        CollisionCircle::drawCircle(painter, m_center, m_fRadius, pen);
    }
}

// CreateRectangle

CreateRectangleCST::CreateRectangleCST(QWidget *view)
    : CollisionShapeTool(view)
{

}

void CreateRectangleCST::enter()
{
    Q_ASSERT(parentView());
    parentView()->setCursor(Qt::CrossCursor);
}

void CreateRectangleCST::exit()
{
    parentView()->setCursor(Qt::ArrowCursor);
}

void CreateRectangleCST::mousePress(QMouseEvent *pEvent)
{
    if(pEvent->buttons() & Qt::LeftButton)
    {
        setInProgress(true);
        m_origin = pEvent->posF();

        transformToEditingViewCoordinates(m_origin);

        emit newObjectCreationStarted();

        m_rect = QRectF(m_origin, QSize(1, 1));
        parentView()->update();
    }
}

void CreateRectangleCST::mouseMove(QMouseEvent *pEvent)
{
    if((pEvent->buttons() & Qt::LeftButton) && isInProgress())
    {
        QPointF mousePos = pEvent->posF();
        transformToEditingViewCoordinates(mousePos);

        m_rect = QRectF(m_origin, mousePos);
        parentView()->update();
    }
}

void CreateRectangleCST::mouseRelease(QMouseEvent *pEvent)
{
    if(isInProgress())
    {
        if(m_rect.width() > 1.0f && m_rect.height() > 1.0f)
        {
            QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
            Q_ASSERT(csd);

            QPointer<CollisionShape> cs = csd->createRectShape(m_rect);
        }
        else
        {
            emit actionCanceled();
        }

        setInProgress(false);
        parentView()->update();
    }
}

void CreateRectangleCST::draw(QPainter &painter) const
{
    CollisionShapeTool::draw(painter);

    if(isInProgress())
    {
        QPen pen(Qt::green, 1, Qt::DotLine);
        CollisionBox::drawRect(painter, m_rect, pen);
    }
}

// CreatePoly

CreatePolyCST::CreatePolyCST(QWidget *view)
    : CollisionShapeTool(view)
{
}

void CreatePolyCST::enter()
{
    Q_ASSERT(parentView());
    parentView()->setCursor(Qt::CrossCursor);
    parentView()->setMouseTracking(true);
    m_poly = QPolygonF();
    m_nCurrentPoint = 0;
}

void CreatePolyCST::exit()
{
    parentView()->setCursor(Qt::ArrowCursor);
    parentView()->setMouseTracking(false);
}

void CreatePolyCST::mousePress(QMouseEvent *pEvent)
{
    if(pEvent->buttons() & Qt::LeftButton)
    {
        QPointF mousePos = pEvent->posF();
        transformToEditingViewCoordinates(mousePos);

        if(m_poly.isEmpty())
        {
            setInProgress(true);
            m_poly.append(mousePos);
            emit newObjectCreationStarted();
        }

        m_poly.append(mousePos);
        m_nCurrentPoint++;
        parentView()->update();
    }
    else if(pEvent->buttons() & Qt::RightButton)
    {
        setInProgress(false);
        // create poly or cancel action
        QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
        Q_ASSERT(csd);

        if(m_nCurrentPoint > 2)
        {
            m_poly.remove(m_poly.size() - 1);
            QPointer<CollisionShape> cs = csd->createPolyShape(m_poly);
        }
        else
        {
            emit actionCanceled();
        }

        // at the end reset poly
        m_poly = QPolygonF();
        m_nCurrentPoint = 0;
        parentView()->update();
    }
}

void CreatePolyCST::mouseMove(QMouseEvent *pEvent)
{
    if(isInProgress())
    {
        QPointF currentPoint = pEvent->posF();
        transformToEditingViewCoordinates(currentPoint);
        m_poly.replace(m_nCurrentPoint, currentPoint);
        parentView()->update();
    }
}

void CreatePolyCST::mouseRelease(QMouseEvent *pEvent)
{

}

void CreatePolyCST::draw(QPainter &painter) const
{
    CollisionShapeTool::draw(painter);

    if(isInProgress())
    {
        QPen pen(Qt::green, 1, Qt::DotLine);
        CollisionPoly::drawPoly(painter, m_poly, pen);
    }
}


SelectCST::SelectCST(QWidget *view)
    : CollisionShapeTool(view)
{
}

void SelectCST::enter()
{
}

void SelectCST::exit()
{
}

void SelectCST::draw(QPainter &painter) const
{
    if(isInProgress() &&
            (m_selectionRect.size().width() > 1) &&
            (m_selectionRect.size().height() > 1))
    {
        painter.setPen(Qt::white);
        painter.setBrush(QColor(255, 255, 255, 128));
        painter.drawRect(m_selectionRect);
    }
}

void SelectCST::mousePress(QMouseEvent *pEvent)
{
    if(pEvent->buttons() & Qt::LeftButton)
    {
//        QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
//        Q_ASSERT(csd);

//        csd->deselectAllShapes();

        QPointF mousePos = pEvent->posF();
        transformToEditingViewCoordinates(mousePos);

        m_origin = mousePos;
        m_selectionRect = QRectF(m_origin, QSize(1, 1));
        setInProgress(true);
    }
}

void SelectCST::mouseMove(QMouseEvent *pEvent)
{
    if((pEvent->buttons() & Qt::LeftButton) && isInProgress())
    {
        QPointF mousePos = pEvent->posF();
        transformToEditingViewCoordinates(mousePos);

        m_selectionRect = QRectF(m_origin, mousePos).normalized();
        parentView()->update();
    }
}

void SelectCST::mouseRelease(QMouseEvent *pEvent)
{
    setInProgress(false);

    QPointer<CollisionShapesDocument> csd = parentView()->collisionShapesDocument();
    Q_ASSERT(csd);

    // find selection
    if(m_selectionRect.width() == 1 && m_selectionRect.height() == 1)
    {
        csd->selectShape(m_selectionRect.topLeft());
    }
    else
    {
        csd->selectShapes(m_selectionRect);
    }

    parentView()->update();
}
